package mac10.roles;

import java.util.ArrayList;
import java.util.LinkedList;

import mac10.connection.MessageConstants;
import mac10.models.Cell;
import mac10.models.Cluster;
import mac10.models.ClusterBook;
import mac10.models.Point;
import mac10.models.WorldModel;
import mac10.util.AStarHeapShepherd;
import mac10.util.Node;
import de.dailab.jiactng.agentcore.ontology.IAgentDescription;


public class Shepherd extends AbstractRole
{

    private Cluster cluster;
//    private Point oldCenter;
    
    public Shepherd (IAgentDescription thisAgent, WorldModel worldModel)
    {
        super(thisAgent, worldModel, true, true, false, false);
        
    }

    public String executeTask (WorldModel worldModel, String task, int posx,
            int posy, long deadline, boolean custom)
    {
      
        long start, end;
        cluster = ClusterBook.staticClusterBook.getCluster(task);
        if(cluster == null)
        {
            System.err.println("NO CLUSTER "+thisAgent.getName());
            return MessageConstants.ACTION_SKIP;
        }
        
        if(cluster.getStart()==null) {
          System.err.println("\n\t### ### ###");
          System.err.println("\tNo start to corall for cluster: "+cluster.getId());
          System.err.println("\tshepherd: "+this.thisAgent.getName());
          System.err.println("\n");
        }
        
        start = System.currentTimeMillis();
        Node node = cluster.assurePath(worldModel, deadline-200);
        end = System.currentTimeMillis();
        if(end-start>0)
          System.err.println(" ## cluster.assurePath.time: "+(end-start));
        
        start = System.currentTimeMillis();
        Point drivePoint = cluster.getDrivePoint(worldModel, thisAgent, deadline);
        end = System.currentTimeMillis();
        if(end-start>0)
          System.err.println(" ## cluster.getDrivePoint.time: "+(end-start));

        
        if(drivePoint == null)//FIXME
        {
            System.err.println("NO DRIVEPOINT "+thisAgent.getName()+"+++++++++++++++++++++++++++++++++++++++++++++++");
            return MessageConstants.ACTION_SKIP;
        }
        
        start = System.currentTimeMillis();
        Point target = findNearestAccesiblePoint(worldModel, drivePoint);
        end = System.currentTimeMillis();
        if(end-start>0)
          System.err.println(" ## cluster.findNearestAccesiblePoint.time: "+(end-start));

        start = System.currentTimeMillis();
        String action = getAction(posx, posy, deadline, target);
        end = System.currentTimeMillis();
        if(end-start>0)
          System.err.println(" ## cluster.getAction.time: "+(end-start));

        start = System.currentTimeMillis();
        registerCowFences(worldModel, node);
        end = System.currentTimeMillis();
        if(end-start>0)
          System.err.println(" ## cluster.registerCowFences.time: "+(end-start));

        return action;
    }
    
    private Point findNearestAccesiblePoint (WorldModel worldModel,
            Point p)
    {
        if(worldModel.getCell(p.getX(), p.getY()).isAccessible())
            return p;
        
        ArrayList<Cell> neighbors = worldModel.getNeighbours(p);
        for (Cell cell : neighbors)
        {
            if(cell.isAccessible())
                return new Point(cell);
        }
        
        return p;
    }

    public LinkedList<String> findPath(WorldModel worldModel, int startx,
            int starty, int goalx, int goaly, long deadline,
            boolean avoidAgents, boolean avoidCows, boolean avoidOpenFences, boolean avoidClosedFences)
    {
        Point start = new Point(startx, starty);
        Point goal = new Point(goalx, goaly);
        if (start.equals(goal))
        {
            LinkedList<String> skip = new LinkedList<String>();
            skip.add(MessageConstants.ACTION_SKIP);
            return skip;
        }
        if (goal.equals(this.lastTarget)) 
        {
            if (checkPath(this.actions, startx, starty, 2))
            {
                return this.actions;
            }
        }
        //Neues Ziel deshalb von allen switchen abmelden
        

        MasterSwitcher.staticMasterSwitcher.deregister(thisAgent);
        this.lastTarget = goal;
        
        AStarHeapShepherd astar = new AStarHeapShepherd(worldModel, start, goal, cluster.getList());
        astar.setAvoidAgents(avoidAgents);
        astar.setAvoidCows(avoidCows);
        astar.setAvoidOpenFences(avoidOpenFences);
        astar.setAvoidClosedFences(avoidClosedFences);
//      long time = System.currentTimeMillis();
        Node to = astar.AStar(deadline);
        // Node to = AStarHeapStatic.AStar(this.worldModel,start, goal);
//      System.err.println("Time needed for A*: " +
//              (System.currentTimeMillis()-time));
        LinkedList<String> actions = null;
        try {
            actions = AbstractRole.nodeToActionStrings(to);
        } catch (Exception e) {
            actions = null;
        }
        
        // Anmeldung des ersten fence auf dem Weg
        Cell fence = getFirstFenceCellOnPath(to);
        if (fence != null)
        {
            MasterSwitcher.staticMasterSwitcher.register(thisAgent, fence);
        }
        
        return actions;
    }
    
    private void registerCowFences (WorldModel worldModel, Node node)
    {
        Node curr = node;
        int counter = 0;
        boolean fenceRegistered = false;
        while(curr != null && curr.getPredecessor() != null && counter < 5) //FIXME null pointer auf demo2
        {
            curr = curr.getPredecessor();
            Cell cell = worldModel.getCell(curr.getX(), curr.getY());
            if(cell.isFence())
            {
                MasterSwitcher.staticMasterSwitcher.register(thisAgent, cell);
                fenceRegistered = true;
            }
            if(fenceRegistered)
                counter++;
        }
    }

//    private Point calcDrivePoint(WorldModel worldModel, 
//            ArrayList<Point> drivePoints, int posx, int posy)
//    {
//        if(drivePoints == null) //FIXME
//        {
//        	System.err.println("Shepherd.calcDrivePoint drivePoints null");
//            return null;
//        }
//        
//        Point[] points = new Point[drivePoints.size()];
//        points = drivePoints.toArray(points);
//        final Point position = new Point(posx, posy);
//        Arrays.sort(points, new Comparator<Point>()
//                {
//                    public int compare (Point a, Point b)
//                    {
//                        int distA = a.maxNorm(position);
//                        int distB = b.maxNorm(position);
//                        if (distA == distB)
//                            return 0;
//                        else if (distA > distB)
//                            return 1;
//                        else
//                            return -1;
//                    }
//                });
//        
//        for (int i = 0; i < points.length; i++)
//        {
////            ArrayList<Cell> neighbors = worldModel.getNeighbours(points[i]);
//            boolean hasShepherd = false;
////            for (Cell cell : neighbors)
////            {
////                if( cell.isAllyAgent() && !position.equals(cell) )
////                {
////                    hasShepherd = true;
////                    break;
////                }
////            }
//            Cell cell = worldModel.getCell(points[i]);
//            if( cell.isAllyAgent() && !position.equals(cell) )
//            {
//                hasShepherd = true;
//            }
//            
//            if(!hasShepherd && !WorldModel.staticWorldModel.agentTargets.containsValue(points[i]))
//            {
//                return points[i];
//            }
//        }
//        
//        return points[0];
//    }

    public String toString ()
    {
        return "shepherd";
    }

}
